using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using SpaceShooter.Utilities;

using System.Diagnostics;


namespace SpaceShooter.Components
{
    /// <summary>
    /// Component di gestione di tutte le istanze delle armi sparate durante la partita.
    /// </summary>
    public class AsteroidsManagerComponent : Microsoft.Xna.Framework.GameComponent
    {
        public static StateManagement.GameState gs;
        public event Action<Vector2, Vector2> onNewRock;
        public static PlayerComponent player;

        public AsteroidsManagerComponent(Game game)
            : base(game)
        {
            gs = (StateManagement.GameState)Game.Services.GetService(typeof(StateManagement.GameState));
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            player = (PlayerComponent)Game.Services.GetService(typeof(PlayerComponent));
            base.Initialize();
        }

        /// <summary>
        /// Aggiornamento dello stato di tutti gli asteroidi presenti.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            if (!gs.pause)
            {
                UpdateAsteroids(gs.asteroids);
            }
            base.Update(gameTime);
        }

        /// <summary>
        /// Aggiorna tutte le istanze della classe Asteroid presenti nella lista
        /// Ed elimina quelle che hanno terminato il loro compito
        /// </summary>
        public void UpdateAsteroids(LinkedList<Asteroid> list)
        {
            Asteroid[] temp = new Asteroid[list.Count];
            int i = 0;

            //Aggiorna tutte le istanze della classe Asteroid presenti nella lista
            foreach (Asteroid x in list)
            {
                if (!gs.timeTraveling)
                {
                    x.Update();
                    if (x.damageDirection != Vector2.Zero)
                    {
                        onNewRock(x.position, x.damageDirection);
                        x.damageDirection = Vector2.Zero;
                    }
                    if (!Utility.IsInScreenSpace(Utility.newRectangleFromCenterPosition(x.position, 30, 30)))
                        x.lifeState = LifeState.DEAD;
                }
                else
                    x.TimeTravel();

                if (x.delete)
                {
                    temp[i] = x;
                    i++;
                }
            }

            //Elimina le istanze della classe Asteroid che hanno terminato il loro ciclo di vita
            for (i = 0; i < temp.Length && temp[i] != null; i++)
            {
                list.Remove(temp[i]);
            }
        }

        public void onNewAsteroidHandler(int XPosition, int speed, int damage)
        {
            float rotation = (float)Utility.NextRandom(0, 50) / (float)Utility.NextRandom(1, 100);
            float XDirection = 0;
            if (XPosition < 20)
                XDirection = 0.5f;
            else if (XPosition > 460)
                XDirection = -0.5f;
            gs.asteroids.AddFirst(new Asteroid(new Vector2(XPosition, 0), Vector2.Normalize(new Vector2(XDirection, 1)), speed, damage, rotation));
        }

        #region Asteroid
        public class Asteroid
        {
            private LinkedList<AsteroidState> states;
            public Vector2 position;
            public Vector2 direction;
            public int life;
            public bool delete;
            public int deathCounter;
            public int speed;
            public LifeState lifeState;
            public float rotation;
            public float rotate;
            public Vector2 damageDirection = Vector2.Zero;

            public LinkedList<AsteroidState> GetStates()
            {
                return states;
            }
            public void SetStates(AsteroidState[] newStates)
            {
                states = new LinkedList<AsteroidState>(newStates);
            }

            public Asteroid()
            {
            }

            public Asteroid(Vector2 position, Vector2 direction, int speed, int damage, float rotation)
            {
                this.position = position;
                this.direction = direction;
                this.life = damage;
                this.speed = speed;
                this.rotation = rotation;
                rotate = (float)Utility.NextRandom(-20, 20) / 1000;


                delete = false;
                deathCounter = 0;
                lifeState = LifeState.NORMAL;
                states = new LinkedList<AsteroidState>();
            }

            public void Damage(int value, Vector2 Direction)
            {
                damageDirection = Vector2.Normalize(Direction);
                life -= value;
                if (life <= 0)
                    lifeState = LifeState.DEAD;
            }

            public void Update()
            {
                if (lifeState != LifeState.DEAD)
                {
                    this.position.X += this.direction.X * speed;
                    this.position.Y += this.direction.Y * speed;
                    rotation = rotation + rotate;
                    states.AddFirst(new AsteroidState(new Vector2(position.X, position.Y), life, lifeState, rotation));
                    if (states.Count > gs.timeTank + 1)
                    {
                        states.RemoveLast();
                    }
                }
                else
                {
                    if (states.Count > 0)
                    {
                        if (states.Count + deathCounter > gs.timeTank)
                            states.RemoveLast();
                    }
                    else
                    {
                        delete = true;
                    }
                    deathCounter++;
                }
            }

            public void TimeTravel()
            {
                if (states.Count > 0)
                {
                    if (deathCounter > 0)
                        deathCounter--;
                    else
                    {
                        AsteroidState st = states.First.Value;
                        position = st.position;
                        lifeState = st.lifeState;
                        life = st.life;
                        rotation = st.rotation;
                        states.RemoveFirst();
                    }
                }
                else
                {
                    delete = true;
                }
            }
        }
        #endregion

        #region AsteroidState

        public class AsteroidState
        {
            public Vector2 position;
            public float rotation;
            public LifeState lifeState;
            public int life;

            public AsteroidState() { }

            public AsteroidState(Vector2 Position, int Life, LifeState LifeState, float Rotation)
            {
                position = Position;
                lifeState = LifeState;
                life = Life;
                rotation = Rotation;
            }
        }

        #endregion
    }
}
